Skip to main content

全局中间件之 TrimStrings

简介

上一章,我们知道了 第二个 全局中间件 ValidatePostSize 作用:根据 php.ini 中的 post_max_size 值对请求体的大小进行判断,截取过大异常,并抛出 413 异常。

这一章,我们来看 第三个 全局中间件 TrimStrings

通过字面意思我们可以看出,是对请求内容进行 前后空白字符清理

file

核心作用:清理 $_GET$_POST 两个数组值的前后空白字符。

中间件 App\Http\Middleware\TrimStrings

首先说一点,此中间件的 handle 方法,没有在当前中间件类中,而是在它的 爷爷 类中。

App\Http\Middleware\TrimStrings

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;

class TrimStrings extends Middleware
{
/**
* 排除指定字段名(key)的值,即以下面为 key 的 value 将不进行空白字符清理
*/
protected $except = [
'password',
'password_confirmation',
];
}

爸爸类 Illuminate\Foundation\Http\Middleware\TrimStrings

<?php

namespace Illuminate\Foundation\Http\Middleware;

class TrimStrings extends TransformsRequest
{
protected $except = [
//
];

/**
* 核心方法:空白字符串清理,此方法将在 handle 中最终调用
*/
protected function transform($key, $value)
{
// 首先排除掉不进行空白字符串的键值对
if (in_array($key, $this->except, true)) {
return $value;
}

// trim 内置函数,进行值空白字符清理工作
return is_string($value) ? trim($value) : $value;
}
}

爷爷类 Illuminate\Foundation\Http\Middleware\TransformsRequest 中的 handle 方法

public function handle($request, Closure $next, ...$attributes)
{
// 传递给此中间件的附加属性,索引数组。
$this->attributes = $attributes;
// 清理 $_GET 和 $_POST 的前后空白字符
$this->clean($request);
// 进入下一个中间件
return $next($request);
}

关于 $attributes 如何传递过来的,看下图

file

file

在类名的后面拼接 ':参数1,参数2,参数3,...,参数n' 这种格式的字符串,即可传入到中间件 handle 方法中,以索引数组的形式哦。

但是,我要命,没找到传过来的附加属性有什么用,,难道让我们在 App\Http\Middleware\TrimStrings 中间件中自己写方法? 可能是吧。

我们继续看 $this->clean($request)

protected function clean($request)
{
// 清理 $_GET 中值的空白符
$this->cleanParameterBag($request->query);

// $_POST 传递方式是不是通过 JSON 进行传递的
if ($request->isJson()) {
// 是通过JSON传递的,那么转成数组,再清理
$this->cleanParameterBag($request->json());
} else {
// 不是通过JSON传递的,直接清理 $_POST 空白符
$this->cleanParameterBag($request->request);
}
}

$request->isJson() 怎么知道是不是通过 JSON 传递数据的?其根本原理,是搜索 请求头 CONTENT_TYPE 内容有没有 ['/json', '+json'] 这两种字符串,有则是 JSON 传递,没有则不是。

通过上面,我们看到核心清理在 cleanParameterBag 方法中。

protected function cleanParameterBag(ParameterBag $bag)
{
$bag->replace($this->cleanArray($bag->all()));
}

看着是不是头大,一堆方法调用。

先看 ParameterBag $bag 是什么鬼:$bag,相信前面看过 Laravel 请求对象之 SymfonyRequest 童鞋,应该了解。这里面就封装了 $_GET, $_POST, $_COOKIE, $_SERVER 的数据,当然这 4 个不是封装在一起,而是分开封装的,每一个封装一个 ParameterBag $bag

我们再回看 $this->cleanParameterBag($request->query); 这句,看到参数 $request->query 吗,它返回的就是封装成 ParameterBag $bag$_GET。而 $request->request 是封装成 ParameterBag $bag$_POST

其中 $bag->all() 返回的就是 $_GET 或者 $_POST 的原生数组,当然是没清理空白字符的旧数据啦

$bag->replace($newGETorPOST) 是指以 $newGETorPOST 为新数据,替换旧的 $_GET$_POST

好了,既然要替换,我们首先要有新的 GETorPOST ,它是怎么来的呢

就是通过 $this->cleanArray($bag->all())

protected function cleanArray(array $data)
{
return collect($data)->map(function ($value, $key) {
return $this->cleanValue($key, $value);
})->all();
}

看到这,是不是又蒙啦。其实很简单了,collect($data) 返回的是 Eloquent 集合。

集合方法 map 的使用

集合方法 all 的使用

好了,既然知道 map 方法就是迭代数组元素,传入 $this->cleanValue($key, $value); 进行处理,那么我们就看一下这个方法

protected function cleanValue($key, $value)
{
if (is_array($value)) {
return $this->cleanArray($value);
}

return $this->transform($key, $value);
}

看到没,之前我说过,$this->transform($key, $value); 这个核心方法,在这里进行了调用。

上面的 if 判断是进行多层数组的处理。

好了,到这里,这个中间件的作用,就讲完了。